package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

type Result struct {
	Code      int
	Message   string
	RequestId string
}

var wg sync.WaitGroup
var curCount int64
var successCount int64
var failCount int64
var n = flag.Int64("n", 100, "请求总数")
var c = flag.Int64("c", 10, "连接数")
var url = flag.String("u", "", "请求地址")
var method = flag.String("m", "GET", "请求方式")
var sdata = flag.String("d", "", "json数据")
var file = flag.String("f", "", "json数据文件")
var innerFailCount int64
var ErrReq = errors.New("code 错误")

var dataList []string // 文件解析后获取的数据
var dataListIndex int
var mu sync.Mutex

func main() {

	flag.Parse()
	fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "开始测试 sdata", *sdata)
	if *method == "POST" {
		if *file == "" {
			log.Fatal("POST请求需要输入数据文件")
		} else {
			readFile()
		}
	}
	var i int64
	var start = time.Now().UnixMilli()

	for i = 0; i < *c; i++ {
		wg.Add(1)
		go TestList(*url)

	}
	wg.Wait()
	fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "main is finished")
	PrintResult(start)
}

func SendRequest(url string, data []byte) error {

	var req *http.Request
	if *method == "GET" {
		req, _ = http.NewRequest(*method, url, nil)
	} else {
		req, _ = http.NewRequest(*method, url, bytes.NewBuffer(data))
	}
	req.Header.Set("Content-Type", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return err
	}
	var result Result
	json.Unmarshal(body, &result)
	// fmt.Println("result", result)
	if result.Code < 0 {
		return ErrReq
	}
	return nil
}

func TestList(url string) {
	var i int64
	var count = (*n) / (*c)
	for i = 0; i < count; i++ {
		// data := utils.GenerateData()
		data := dataList[getIndex()]
		// fmt.Println(string(data))
		err := SendRequest(url, []byte(data))
		atomic.AddInt64(&curCount, 1)
		if err != nil {
			if err == ErrReq {
				atomic.AddInt64(&innerFailCount, 1)
			} else {
				atomic.AddInt64(&failCount, 1)
			}
		} else {
			atomic.AddInt64(&successCount, 1)
		}

		if curCount%count == 0 && curCount > 0 {
			fmt.Println("当前请求数：", curCount)
		}
	}
	wg.Done()
}

func PrintResult(start int64) {
	fmt.Println("=======测试结果=========")
	var endTime = time.Now().UnixMilli()
	var costTime = float64(endTime-start) / float64(1000)
	fmt.Printf("总请求数：%d \n", *n)
	fmt.Printf("连接数：%d \n", *c)
	fmt.Printf("成功数量：%d,比例：%.2f%% \n", successCount, float64(successCount)/float64(*n)*100)
	fmt.Printf("失败数量：%d,比例：%.2f%% \n", failCount, float64(failCount)/float64(*n)*100)
	fmt.Printf("业务失败数量%d,比例：%.2f%% \n", innerFailCount, float64(innerFailCount)/float64(*n)*100)
	fmt.Printf("请求总耗时：%.4fs\n", costTime)
	fmt.Printf("每秒处理的请求数QPS: %.4f", float64(*n)/float64(costTime))
}

func getIndex() int {
	mu.Lock()
	var i = dataListIndex
	dataListIndex++
	mu.Unlock()
	return i

}

func readFile() {
	bs, err := ioutil.ReadFile(*file)
	if err != nil {
		fmt.Println(err)
		panic(err)
	}
	content := string(bs)

	dataList = strings.Split(content, "\n")

}
